#ifndef CIRCULARBUFFER_H
#define CIRCULARBUFFER_H

#include <cstring>
#include <memory>
#include <stdexcept>

template <typename T>
class CircularBuffer
{
private:
    T *buffer;
    T *start;
    T *end;
    T *write_ptr;

public:
    const int capacity;

public:
    explicit CircularBuffer(int size) : capacity(size)
    {
        if constexpr (std::is_default_constructible<T>::value)
        {
            buffer = new T[capacity];
        }
        else
        {
            buffer = static_cast<T *>(operator new[](capacity * sizeof(T)));
            memset(buffer, 0, capacity * sizeof(T));
        }
        start = buffer;
        end = buffer + capacity;
        write_ptr = buffer;
    }

    ~CircularBuffer()
    {
        delete[] buffer;
    }

    void push_back(const T &value)
    {
        *write_ptr = value;
        write_ptr++;
        if (write_ptr == end)
        {
            write_ptr = start;
        }
    }

    T &operator[](int index)
    {
        if (index >= capacity || index < -capacity)
        {
            throw std::out_of_range("CircularBuffer: Index out of range");
        }
        T *index_ptr = write_ptr + index;
        if (index_ptr >= end)
        {
            index_ptr -= capacity;
        }
        else if (index_ptr < start)
        {
            index_ptr += capacity;
        }
        return *index_ptr;
    }

    const T &operator[](int index) const
    {
        if (index >= capacity || index < -capacity)
        {
            throw std::out_of_range("CircularBuffer: Index out of range");
        }
        T *index_ptr = write_ptr + index;
        if (index_ptr >= end)
        {
            index_ptr -= capacity;
        }
        else if (index_ptr < start)
        {
            index_ptr += capacity;
        }
        return *index_ptr;
    }

    void clear()
    {
        if constexpr (std::is_default_constructible<T>::value)
        {
            if constexpr (std::is_copy_assignable<T>::value)
            {
                std::fill(buffer, buffer + capacity, T());
            }
            else
            {
                std::uninitialized_fill(buffer, buffer + capacity, T());
            }
        }
        else
        {
            memset(buffer, 0, capacity * sizeof(T));
        }
        write_ptr = buffer;
    }
};

#endif // CIRCULARBUFFER_H